Skip to content

Opaque PyObject ABI support#5807

Draft
ngoldbaum wants to merge 20 commits intoPyO3:mainfrom
ngoldbaum:opaque-pyobject
Draft

Opaque PyObject ABI support#5807
ngoldbaum wants to merge 20 commits intoPyO3:mainfrom
ngoldbaum:opaque-pyobject

Conversation

@ngoldbaum
Copy link
Contributor

@ngoldbaum ngoldbaum commented Feb 13, 2026

Towards fixing #5786.

This is done on top of #5753 because that PR is needed to make this work.

With some help from @davidhewitt, this almost works but subclassing seems to be broken.

---- pycell::tests::test_pyref_as_super stdout ----
[src/pycell.rs:833:13] &pyref.inner = <builtins.SubSubClass object at 0x104ee6940>
[src/pycell.rs:834:13] &pyref.as_super().inner = <builtins.SubSubClass object at 0x104ee6940>

thread 'pycell::tests::test_pyref_as_super' (263860852) panicked at src/pycell.rs:835:13:
assertion `left == right` failed
  left: 20
 right: 10

I suspect using Layout to fill in LayoutAsBase is incorrect here:

type LayoutAsBase = <Self as #pyo3_path::impl_::pyclass::PyClassImpl>::Layout;

But I'm not sure how to update the macros and the macro wrappers to get that to work correctly.

@Icxolu do you happen to have any insight about what's going wrong?

TODO:

  • Hide PyModuldeDef in the FFI bindings.
  • Make tests fully pass

@ngoldbaum ngoldbaum added the CI-no-fail-fast If one job fails, allow the rest to keep testing label Feb 13, 2026
@ngoldbaum
Copy link
Contributor Author

Interesting: the careful tests fail in exactly the same way as the tests fail on the opaque pyobject build. Maybe a useful hint that there's some unsafety happening somewhere...

@ngoldbaum
Copy link
Contributor Author

the careful tests fail in exactly the same way as the tests fail on the opaque pyobject build.

It looks like all the tests on Python 3.12 and newer fail the abi3,full,multiple-pymethods tests. So it looks like the issue is using a PyVarObject in the limited API. Before this PR the base type is still a static PyObject.

As @davidhewitt pointed out when I chatted with him last Friday - it's worth checking to see if doing that introduces performance regressions. If it does, we need to make sure we set things up so a PyVarObject base type is only used on opaque pyobject builds.

codspeed uses the free-threaded build so it's not testing this configuration at all. I need to run benchmarks locally to see what the performance impact is. We also need a benchmark for creating PyClass instances - there is a benchmark for type initialization but not for instance initialization as far as I can see.

@ngoldbaum
Copy link
Contributor Author

ngoldbaum commented Feb 16, 2026

I opened #5816 with a benchmark for creating an instance of a PyClass. I also had to hack in abi3 support for the benchmark crate, see #5818.

Running the bench_pyclass_create benchmark from #5816, I see the following results on Python 3.12.12:

cargo bench --manifest-path=pyo3-benches/Cargo.toml --features=abi3 bench_pyclass_create

With opaque PyObject:
bench_pyclass_create    time:   [42.311 ns 42.374 ns 42.441 ns]

Without:
bench_pyclass_create    time:   [40.705 ns 40.790 ns 40.882 ns]

I ran the benchmarks on my personal development machine so there's some noise in the results. I'm showing the best of four runs of this benchmark for each commit. I see similar results on 3.15a6.

Very roughly, there's a 5% performance cost. That's on the bubble for being significant but probably is too steep to force extensions to pay that cost on the 3.12 stable ABI.

I think I'll go ahead and adjust this PR to only use PyVarObject as a base when it's absolutely needed when _Py_OPAQUE_PYOBJECT is defined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI-build-full CI-no-fail-fast If one job fails, allow the rest to keep testing free-threading proc-macro

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant